﻿using System;
using System.Collections.Generic;
using System.Text;
using WsProtocol.IEC62056.DataTypeClass;
using Comm.PublicClass;

namespace WsProtocol.IEC62056.CosemACSE
{
    // 链路层和应用层连接类
    public class DLL_Connect {
        private static string useTag = "8180";

        public static string Request(CosemObject OI, ref string errorinfo)
        {
            string FormatValue = string.Empty;
            try
            {
                StringBuilder stbStr = new StringBuilder();

                //{
                //发送最大信息域长度 
                //接收最大信息域长度 
                //发送窗口大小
                //接收窗口大小
                //}
                if (OI.DataNode != null)
                {

                    string stmp = MyConvert.DecToHex(OI.DataNode.eleNodeList[0].SetValue);
                    stmp = stmp.Length % 2 > 0 ? "0" + stmp : stmp;
                    string len = MyConvert.DecToHex((stmp.Length / 2).ToString(), 2);
                    FormatValue = "05" + len + stmp;

                    stmp = MyConvert.DecToHex(OI.DataNode.eleNodeList[1].SetValue);
                    stmp = stmp.Length % 2 > 0 ? "0" + stmp : stmp;
                    len = MyConvert.DecToHex((stmp.Length / 2).ToString(), 2);
                    FormatValue += "06" + len + stmp;

                    stmp = MyConvert.DecToHex(OI.DataNode.eleNodeList[2].SetValue, 8);
                    FormatValue += "0704" + stmp;

                    stmp = MyConvert.DecToHex(OI.DataNode.eleNodeList[3].SetValue, 8);
                    FormatValue += "0804" + stmp;

                    FormatValue =useTag +  MyConvert.DecToHex((FormatValue.Length / 2).ToString(), 2) + FormatValue;//

                }
                return FormatValue;
            }
            catch (Exception ex) {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        public static int Response(ref string frameData, CosemObject OI, ref string errorinfo)
        {
            int iResult = 0;
            errorinfo = "";
            try
            {
                int pos = frameData.IndexOf(useTag);
                if (pos >= 0)
                {
                    //用户信息参数只有当结果为NOK时才存在，这时该参数就应该包含不接受连接的原因的用户信息
                    OI.result = ((int)DLLConnResult.NOK).ToString(); 
                    
                    frameData = frameData.Substring(pos + useTag.Length + 2);//去掉用户信息标志位长度和一个长度字节
                    for (int i = 0; i < 4; i++)
                    {
                        int len = Convert.ToInt32(frameData.Substring(2, 2), 16); //第一个字节为各信息的标志
                        frameData = frameData.Substring(4);
                        OI.DataNode.eleNodeList[i].FrameValue = frameData.Substring(0, len * 2);
                        OI.DataNode.eleNodeList[i].ReadValue = MyConvert.HexToDec(OI.DataNode.eleNodeList[i].FrameValue);
                        frameData = frameData.Substring(len * 2);
                    }
                }
                else
                    OI.result = ((int)DLLConnResult.OK).ToString(); 
                
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                iResult = -1;
            }
            return iResult;
        }
    }
    
    public class DLL_Release
    {

        public static string Request()
        {
            return string.Empty;
        }

        public static int Response()
        {
            return 0;
        }
    }

    //初步代码处理完成
    public class ACSE_Connect
    {       
         /// <summary>
         /// AARQ ::= [APPLICATION 0] IMPLICIT SEQUENCE 
        //{
        //-- [APPLICATION 0] == [ 60H ] = [ 96 ] 
        //application-context-name [1] Application-context-name,

        //-- 如果只使用内核，下面的字段不出现
        //sender-acse-requirements [10]    IMPLICIT ACSE-requirements OPTIONAL, BIT STRING {authentication(0)}
        //    -- 如果选用身份验证单元，下面的字段应出现。
        //mechanism-name [11]    IMPLICIT Mechanism-name OPTIONAL,  使用何种Authentication 如果选用身份验证单元，下面的字段应出现。
            //calling-authentication-value [12]    EXPLICIT Authentication-value OPTIONAL, 指出Authentication的值（即密码）
            //implementation-information [29] IMPLICIT Implementation-data OPTIONAL, GraphicString 
        //user-information [30]  EXPLICIT Association-information OPTIONAL OCTET STRING
        //}
        public static string Request(CosemObject OI, Encryption encryption, ref string errorinfo)
        {
            string FormatValue = string.Empty; 
            List<ElementData> eleNodeList= OI.DataNode.eleNodeList;
            FormatValue = AppContextName.GetFormatValue(eleNodeList[0],ref  errorinfo);
            FormatValue += APtitle.GetFormatValue(encryption, ref errorinfo);
            FormatValue += MechanismName.GetFormatValue(eleNodeList[1],ref errorinfo);
            FormatValue += AA_UserInfo.GetFormatValue(eleNodeList[2],encryption,ref errorinfo);
            string len = BERiLen.GetFormatValue(FormatValue.Length/2);
            return MyConvert.DecToHex(((int)ClientApduSel.ConnectRequest).ToString(), 2) + len + FormatValue;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="frameData"></param>
        /// <param name="OIList"></param>
        /// <param name="errorinfo"></param>
        /// <returns></returns>
        public static int Response(ref string frameData, CosemObject OI, Encryption encryption, ref string errorinfo)
        {
            int iResult = 0;
            try
            {
                if (string.IsNullOrEmpty(frameData))
                {
                    errorinfo = "解析数据为空";
                    return -1;
                }
                int ilen = BERiLen.AnalizeData(ref frameData, ref errorinfo);

                List<ElementData> eleNodeList= OI.DataNode.eleNodeList;
                iResult = AppContextName.AnalizeData(ref frameData, eleNodeList[0], ref errorinfo);

                #region result 连接结果,前面四字节为XDR编码
                //A2 //Tag of Association-result,  03 //Len of Association-result, 
                //02 //type = structure，   01 //length = 1
                int connectResult = Convert.ToInt16(frameData.Substring(8, 2), 16); //result 连接结果
                 frameData = frameData.Substring(10);

                 if (connectResult != 0)
                 {
                     iResult = 0;
                     OI.result = connectResult.ToString() + ":" + ((AssociationResult)connectResult).ToString();
                                // Enum.GetName(typeof(AssociationResult), (AssociationResult)connectResult);// +connectResult.ToString();                     
                 }
                 else
                 {
                     iResult = 0;
                     OI.result = connectResult.ToString();
                 }
                #endregion
                #region Associate-source-diagnostic
                 //A3 05 A1 03 02 01 00 Associate-source-diagnostic 只要取01 00两字节,前面字节为XDR编码
                 string ifAppend = frameData.Substring(10, 2); //Associate-source-diagnostic的选择结果

                 frameData = frameData.Remove(0, 12);
                 string sresult = MyConvert.HexToDec(frameData.Substring(0, 2));
                 frameData = frameData.Substring(2); //加密情况下成功会固定返回OE，可以检测也可以不检测
                 if (sresult != "0" && sresult != "14")
                 {
                     if (ifAppend == "01") //acse-service-user			[1]
                     {
                         sresult = sresult + ":" + Enum.GetName(typeof(EnuAerviceUser), (EnuAerviceUser)int.Parse(sresult));
                     }
                     else //acse-service-provider		[2]
                     {
                         sresult = sresult + ":" + Enum.GetName(typeof(EnuServiceProvider), (EnuServiceProvider)int.Parse(sresult));
                     }

                     OI.result = OI.result + "," + sresult;
                 }
                 #endregion

                # region responding-AP-title
                 iResult = APtitle.AnalizeData(ref frameData, encryption, ref errorinfo);
                #endregion
                 bool exists = AcseRequire.AnalizeData(ref frameData, ref errorinfo); 
                if(exists)
                   iResult =  MechanismName.AnalizeData(ref frameData, eleNodeList[1],encryption, ref errorinfo);               
              
                #region user-information  BE
                iResult = AA_UserInfo.AnalizeData(ref frameData, eleNodeList[2], encryption, ref errorinfo);
               #endregion

            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                iResult = -1;
            }
            return iResult;

        }
    }  
 
    public class ACSE_Release
    {
        public static string Request(CosemObject OI, Encryption encryption, ref string errorinfo)
        {
            string FormatValue = string.Empty; 
            List<ElementData> eleNodeList = OI.DataNode.eleNodeList;
            FormatValue = ReleaseReason.GetFormatValue(eleNodeList[0], ref errorinfo); 

            if (eleNodeList.Count > 0)
            {
                FormatValue += AA_UserInfo.GetFormatValue(eleNodeList[1], encryption, ref errorinfo);                
            }
            string len = BERiLen.GetFormatValue(FormatValue.Length / 2);
            return MyConvert.DecToHex(((int)ClientApduSel.ReleaseRequest).ToString(), 2) + len + FormatValue;
        }

        public static int Response(ref string frameData, CosemObject OI, Encryption encryption, ref string errorinfo)
        {
            int iResult = 0;
            try
            {
                if (string.IsNullOrEmpty(frameData))
                {
                    errorinfo = "解析数据为空";
                    return -1;
                }
                int ilen = BERiLen.AnalizeData(ref frameData, ref errorinfo);
                List<ElementData> eleNodeList = OI.DataNode.eleNodeList;
                iResult = ReleaseReason.AnalizeData(ref frameData, eleNodeList[0], ref errorinfo);
                               
                #region user-information  BE
                iResult = AA_UserInfo.AnalizeData(ref frameData, eleNodeList[1], encryption, ref errorinfo);
                #endregion

            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                iResult = -1;
            }
            return iResult;
        }
    }

    public class ReleaseReason {
        private static string RRTag = "80"; // [0]//reasion Normal 的编码 80标志 01 长度 00代表Normal原因
    
        public static string GetFormatValue(ElementData eleNode, ref string errorinfo)
        {
            string FormatValue = string.Empty;
            try
            {
                if (!string.IsNullOrEmpty(eleNode.SetValue))
                {
                    FormatValue = MyConvert.DecToHex(eleNode.SetValue, 2);
                    FormatValue = RRTag + "01" + FormatValue; //01为数据长度
                }
                return FormatValue;
            }                
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }

        }

        public static int AnalizeData(ref string frameData,ElementData eleNode, ref string errorinfo)
        {
            int iresult = 0;
            try
            {
                if (string.IsNullOrEmpty(frameData)) return 0;
                if (frameData.Substring(0, 2) == RRTag)
                {
                    string reason = frameData.Substring(RRTag.Length + 2, 2);
                    eleNode.ReadValue = reason + ((DicResReason)int.Parse(reason)).ToString();
                    frameData = frameData.Substring(6);
                }
                return iresult;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return -1;
            }
        }

    }
    public class APtitle { 
        private static string RQTag = "A6"; //calling-AP-title [6]
        private static string RETag = "A4";//responding-AP-title [4]
        private static int systlen = 8;
        public static string GetFormatValue(Encryption encryption, ref string errorinfo)
        {
            try
            {
                if ((encryption.ContextName == RequirContext.LN_Cipher) //|| encryption.ContextName == RequirContext.SN_Cipher)
                || (encryption.apduAuth >= Authentication.HLS)) //加入服务器系统标题
                {
                    //厂商编码加5字节表号 0A为长度，04为字符串编码，08为sys_t长度
                    return RQTag + "0A0408" + encryption.GetSysTitle();
                }
                else
                    return string.Empty;
                
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }

        }

        public static int AnalizeData(ref string frameData, Encryption encryption, ref string errorinfo)
        {
            int iresult = 0;
            try
            {
                if (string.IsNullOrEmpty(frameData)) return 0;
                if (frameData.Substring(0, 2) == RETag) {
                    encryption.Sys_T = frameData.Substring(systlen, systlen * 2);
                    frameData = frameData.Substring((systlen + 4) * 2);
                }                
                return iresult;
            }
            catch (Exception ex) {
                errorinfo = ex.ToString();
                return -1;
            }
        }
    }
    
    public class Conformance
    {
        private static string confTag = "5F1F"; // [APPLICATION 31] tag
        public static string GetFormatValue(ElementData eleNode, ref string errorinfo)
        {
            try
            {
                string FormatValue = eleNode.SetValue.PadLeft(6, '0');//3字节，看界面如何传参
                FormatValue = confTag +                   // encoding the [APPLICATION 31] tag (ASN.1 explicit tag) 
                                "04" +                      // encoding the length of the 'contents' field in octet (4) 
                                "00" +                    // encoding of the number of unused bits in the final octet of the bitstring
                                FormatValue;
                return FormatValue;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }

        }

        public static int AnalizeData(ref string frameData, ElementData eleNode, ref string errorinfo)
        {
            int iresult = 0;
            try
            {
                if (string.IsNullOrEmpty(frameData)) return 0;
                eleNode.ReadValue = eleNode.FrameValue = frameData.Substring(confTag.Length + 4, 6);
                frameData = frameData.Remove(0, confTag.Length + 10);
                return iresult;
            }
            catch (Exception ex) {
                errorinfo = ex.ToString();
                return -1;
            }
        }
    }
    public class AA_UserInfo
    {
        /// <summary>
        /// xDLMS-context-type ::= structure
        //{
        //conformance : bitstring(24) ;
        //max_receive_pdu_size : long-unsigned ;
        //max_send_pdu_size : long-unsigned ;
        //dlms_version_number : unsigned ;
        //quality_of_service : integer;
        //cyphering_info : octet-string 专用密钥参数
        //}
        //xDLMS-Initiate.request 		::= SEQUENCE
        //{
        //dedicated-key 					OCTET STRING OPTIONAL,
        //-- 在没有加密的DLMS中不应编码
        //response-allowed 					BOOLEAN DEFAULT TRUE,
        //proposed-quality-of-service 		[0] IMPLICIT Integer8 OPTIONAL,
        //proposed-dlms-version-number 		Unsigned8,
        //proposed-conformance 				Conformance,
        //client-max-received-pdu-size 		Unsigned16
        //}
       
        private static string Tag = "BE";//user-information [30]的编码        
        private static string IniRQTag = "01"; //Initiate.request tag
        private static string IniRETag = "08"; //Initiate.response tag
        /// </summary>       
        public static string GetFormatValue(ElementData eleNode, Encryption encryption , ref string errorinfo)
        {
            string FormatValue = string.Empty;
            try
            {
                string resallow = "00";//不出现，取默认值
                string confronmance = Conformance.GetFormatValue(eleNode.eleNodeList[0],ref errorinfo);
                string ClientSize = "0000";// longunsigned.GetFormatValue(eleNode.eleNodeList[2], ref errorinfo); //eleNode.eleNodeList[1]保存服务端尺寸大小
                string VerNumber = unsigned.GetFormatValue(eleNode.eleNodeList[3], ref errorinfo);
                string Quality = eleNode.eleNodeList[4].SetValue;
                if (string.IsNullOrEmpty(Quality))
                    Quality = "00";
                else
                    Quality = "01" + integer.GetFormatValue(eleNode.eleNodeList[4], ref errorinfo);

                string cypherInfo = eleNode.eleNodeList[5].SetValue; //密钥 DedicatedKey
                if (string.IsNullOrEmpty(cypherInfo))
                    cypherInfo = "00";
                else
                {
                    cypherInfo = "01" + octString.GetFormatValue(cypherInfo, ref errorinfo);//密钥编码 DedicatedKey
                }

                string InitiateRequest = cypherInfo + resallow + Quality + VerNumber + confronmance + ClientSize;
                InitiateRequest = IniRQTag + InitiateRequest;// 01  ClientApduSel.InitiateRequest

                //if ((encryption.ContextName == RequirContext.LN_Cipher || encryption.apduAuth >= Authentication.HLS)
                //    && encryption.securityPolicy != SecurityPolicy.Nothing) //加密InitiateRequest
                if (encryption.IfEncrypt())
                {
                    encryption.PlaintData = InitiateRequest;
                   
                    InitiateRequest = MyConvert.DecToHex(((int)ClientApduSel.gloInitiateRequest).ToString(), 2)
                        + octString.GetFormatValue(encryption.GCM_AESEncrypt(SecurityPolicy.需认证加密,true, ref errorinfo), ref errorinfo);
                }

                FormatValue = "04" + // encoding the choice for user-information (OCTET STRING, Universal) 
                             MyConvert.DecToHex((InitiateRequest.Length / 2).ToString(), 2) +  //长度
                              InitiateRequest;

                FormatValue = Tag + MyConvert.DecToHex((FormatValue.Length / 2).ToString(), 2) + FormatValue;                

                return FormatValue;
            }
            catch (Exception ex) {
                errorinfo = ex.ToString();
                return FormatValue;
            }
        }
        //xDLMS initiateResponse APDU） 
        //{
        //negotiated-quality-of-service 
        //negotiated-dlms-version-number 
        //negotiated-conformance 
        //server-max-receive-pdu-size 
        //vaa-name 
        //}
        public static int AnalizeData(ref string frameData, ElementData eleNode, Encryption encryption, ref string errorinfo)
        {
            int iresult = 0;            
            try
            {
                if (frameData.Substring(0, 2) != Tag) return iresult;

                frameData = frameData.Substring(2); //去掉Tag BE
                int ilen = Convert.ToInt32(frameData.Substring(0, 2), 16); //10  Len (according to bytes)
                string Valuestr = frameData.Substring(2, ilen * 2);
                frameData = frameData.Substring((ilen + 1) * 2); //包括长度字节本身

                //04 0E data type choice for user-information (OCTET STRING, Universal), Len bytes
                //InitiateResponse tag
                Valuestr = Valuestr.Substring(4);
                if (Convert.ToInt32(Valuestr.Substring(0, 2), 16) == (int)ServerApduSel.gloInitiateResponse) //解密
                {
                    Valuestr = Valuestr.Substring(2);                                 
                    encryption.EncryptData = octString.AnalizeDataSpecial(ref Valuestr, ref errorinfo);                   
                    encryption.PlaintData = string.Empty;
                    Valuestr = encryption.GCM_AESDecrypt(0, ref errorinfo);
                    if (!string.IsNullOrEmpty(errorinfo))
                        return -1;
                }
                Valuestr = Valuestr.Substring(IniRETag.Length);

                string qualityflag = Valuestr.Substring(0, 2);
                Valuestr = Valuestr.Remove(0, 2);
                if (qualityflag == "01")
                    iresult = integer.AnalizeData(ref Valuestr, eleNode.eleNodeList[4], ref errorinfo);//quality-of-service
                iresult = unsigned.AnalizeData(ref Valuestr, eleNode.eleNodeList[3], ref errorinfo); //version-number 
                iresult = Conformance.AnalizeData(ref Valuestr, eleNode.eleNodeList[0], ref errorinfo);         //conformance
                iresult = longunsigned.AnalizeData(ref Valuestr, eleNode.eleNodeList[1], ref errorinfo); //服务端recieve size
                string VAAName = Valuestr; //0007 for LN and FA00 for SN


                return iresult;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return -1;
            }

        }      
    }
    public class AppContextName
    {
        private static string Tag = "A1";
        private static string NameConst = "0906076085740508010";
        public static string GetFormatValue(ElementData eleNode, ref string errorinfo)
        {
            try
            {
                string FormatValue = Tag + NameConst; //固定

                RequirContext Rtype = (RequirContext)Enum.Parse(typeof(RequirContext), eleNode.SetValue);
                FormatValue += ((int)Rtype).ToString();
                return FormatValue;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        public static int AnalizeData(ref string frameData, ElementData eleNode, ref string errorinfo)
        {
            try
            {
                string sName = frameData.Substring((Tag + NameConst).Length, 1);
                eleNode.ReadValue = eleNode.FrameValue = sName;
                frameData = frameData.Substring((Tag + NameConst).Length + 1);
                return 0;
            }
            catch (Exception ex) {
                errorinfo = ex.ToString();
                return -1;
            }
        }

    }

    public class AcseRequire
    {
        private static string ARQTag = "8A";
        private static string ARETag = "88";
        public static string GetFormatValue(bool UseAuth)
        {
            //acse-requirements field component ([10],02为长度，07为位字串中7位无用，只最高位有效，80即最高位为1使用认证
            if (UseAuth)
                return ARQTag + "020780";
            else
                return ARQTag + "020700";

        }
        public static bool AnalizeData(ref string frameData, ref string errorinfo)
        {
            //acse-requirements 88
            try
            {
                bool exists = false;
                if (frameData.Substring(0, 2) == ARETag)
                {                  
                    exists = frameData.Substring(6, 2) == "80";
                    frameData = frameData.Substring(8);
                }

                return exists;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return false;
            }
        }
    }

    public class MechanismName
    {

        private static string MName = "076085740508020";
        private static string MRQTag = "8B";
        private static string MRETag = "89";
        private static string PassRQTag = "AC";
        private static string PassRETag = "AA";
        public static string GetFormatValue(ElementData eleNode, ref string errorinfo)
        {
            string FormatValue = string.Empty;
            try
            {
                Authentication Atype = (Authentication)Enum.Parse(typeof(Authentication), eleNode.SetValue);
                if (Atype != Authentication.NoAuth)
                {
                    string AcseR = AcseRequire.GetFormatValue(true);
                    FormatValue = AcseR + MRQTag + MName + ((int)Atype).ToString();

                    //authentication-value
                    string PassWord = eleNode.eleNodeList[0].SetValue;

                    PassWord = visiblestring.GetFormatValue(PassWord, ref errorinfo); //ASCII码
                    
                    //80 对Authentication-information (charstring [0] IMPLICIT GraphicString)的编码选择
                    PassWord = "80" + PassWord;  
                    //"AC" mechanism-name组件(component [12], Context-specific)标记的编码
                    //标记的组件值字段的长度
                    PassWord = PassRQTag + MyConvert.DecToHex((PassWord.Length / 2).ToString(), 2) + PassWord;

                    FormatValue += PassWord;
                }
                return FormatValue;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return FormatValue;
            }
        }

        public static int AnalizeData(ref string frameData, ElementData eleNode,Encryption encrytion, ref string errorinfo)
        {            
            int iresult = 0;
            try
            {
                if (eleNode.eleNodeList.Count > 0)
                    eleNode.eleNodeList[0].ReadValue = string.Empty;
                else
                    eleNode.eleNodeList.Add(new ElementData());
                string sAuth = "0";
                if (frameData.Substring(0, 2) == MRETag)  //AA authentication-value
                {
                    sAuth = frameData.Substring((MRETag + MName).Length, 1);
                    eleNode.ReadValue = sAuth;
                    frameData = frameData.Substring((MRETag + MName).Length + 1);
                    
                }
                if (frameData.Substring(0, 2) == PassRETag)
                {//AA 0A 80 08 41 42 43 44 45 46 47 48
                    frameData = frameData.Substring(6); //visiblestring
                    iresult = octString.AnalizeData(ref frameData, eleNode.eleNodeList[0], ref errorinfo);
                    if (int.Parse(sAuth) >= (int)Authentication.HLS)
                    { //加密StoC
                        encrytion.PlaintData = eleNode.eleNodeList[0].FrameValue; //对未转成ASCII码的命令数据加密
                        eleNode.eleNodeList[0].ReadValue = encrytion.GCM_AESEncrypt(SecurityPolicy.需认证, false, ref errorinfo);
                    }
                }
                return iresult;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return -1;
            }
        }

    }
}
